 
/* 
 * Copyright (C) Ignacio Lucas
 */
 
#include "conf_file.h"
#include <libconfig.h>
#include "../common.h"
#include "../jobs_controller.h"
#include "../timer_service.h"
#include "../utils/hashmap.h"

static char map_all_pools[BASE_USER_POOLS][OBJECT_POOL_MAX_NAME_LENGTH]={"SOCKET_POOL","QUEUE_POOL","JOB_POOL","TIMER_SERVICE_POOL","HASHMAP_POOL","HASHMAP_HASHENTRY_POOL"};

// funcion que proveera los tamanios de pools de objetos del usuario.
typedef size_t (*user_pool_get_object_size)();

static void init_system_pools(mashup_server_core_config_t *cf)
{
	object_pool_conf_array *pools_array = &(cf->pool_conf);
	object_pool_conf *all_pools = pools_array->el;

	/** El tamanio inicial de pool_array va a ser el que proveen los system pools. **/
	pools_array->count = BASE_USER_POOLS;

	/** Defino los valores default para los system pools. **/
	all_pools[SOCKET_POOL].exclusive=1;
	all_pools[SOCKET_POOL].object_size=socket_get_struct_size();
	all_pools[SOCKET_POOL].incremental_length=256;
	all_pools[SOCKET_POOL].max_incremental_steps=64;
	all_pools[SOCKET_POOL].fixed_length=1*K;
	strcpy(all_pools[SOCKET_POOL].name,map_all_pools[SOCKET_POOL]);

	all_pools[QUEUE_POOL].exclusive=0;
	all_pools[QUEUE_POOL].object_size=queue_get_node_size();
	all_pools[QUEUE_POOL].incremental_length=256;
	all_pools[QUEUE_POOL].max_incremental_steps=64;
	all_pools[QUEUE_POOL].fixed_length=2*K;
	strcpy(all_pools[QUEUE_POOL].name,map_all_pools[QUEUE_POOL]);

	all_pools[JOB_POOL].exclusive=1;
	all_pools[JOB_POOL].object_size=server_worker_get_server_worker_job_struct_size();
	all_pools[JOB_POOL].incremental_length=256;
	all_pools[JOB_POOL].max_incremental_steps=64;
	all_pools[JOB_POOL].fixed_length=2*K;
	strcpy(all_pools[JOB_POOL].name,map_all_pools[JOB_POOL]);

	all_pools[TIMER_SERVICE_POOL].exclusive=0;
	all_pools[TIMER_SERVICE_POOL].object_size=timer_service_get_timer_size();
	all_pools[TIMER_SERVICE_POOL].incremental_length=256;
	all_pools[TIMER_SERVICE_POOL].max_incremental_steps=64;
	all_pools[TIMER_SERVICE_POOL].fixed_length=1*K;
	strcpy(all_pools[TIMER_SERVICE_POOL].name,map_all_pools[TIMER_SERVICE_POOL]);

	all_pools[HASHMAP_POOL].exclusive=1;
	all_pools[HASHMAP_POOL].object_size=hashmap_get_hashmap_size();
	all_pools[HASHMAP_POOL].incremental_length=256;
	all_pools[HASHMAP_POOL].max_incremental_steps=64;
	all_pools[HASHMAP_POOL].fixed_length=256;
	strcpy(all_pools[HASHMAP_POOL].name,map_all_pools[HASHMAP_POOL]);

	all_pools[HASHMAP_HASHENTRY_POOL].exclusive=1;
	all_pools[HASHMAP_HASHENTRY_POOL].object_size=hashmap_get_hashentry_size();
	all_pools[HASHMAP_HASHENTRY_POOL].incremental_length=256;
	all_pools[HASHMAP_HASHENTRY_POOL].max_incremental_steps=64;
	all_pools[HASHMAP_HASHENTRY_POOL].fixed_length=1*K;
	strcpy(all_pools[HASHMAP_HASHENTRY_POOL].name,map_all_pools[HASHMAP_HASHENTRY_POOL]);
}

static object_pool_conf * get_system_pool_ref(object_pool_conf_array *pools_array, const char*system_pool_name)
{
	int i=0;
	for (i=0;i<BASE_USER_POOLS;i++)
	{
		if (strcmp(map_all_pools[i],system_pool_name)==0)
		{
			return &(pools_array->el[i]);
		}
	}
	return NULL;
}


static void apply_int_value(int *destination, int is_value_found, int value)
{
	if ((is_value_found == CONFIG_TRUE) && (value > 0))
	{
		*destination=value;
	}
}

static void CONF_FILE_APPLY_INT_VALUE(int *dest,const config_setting_t *pool_conf,const char *field,int *readval)
{
	apply_int_value(dest,config_setting_lookup_int(pool_conf,field,readval),*readval);
}


static int conf_parse_system_pools(mashup_server_core_config_t *cf, config_setting_t *pool_setting)
{
	int count = config_setting_length(pool_setting), i=0;
	const config_setting_t *pool_conf = NULL;
	const char *name;
	int incremental_length=-1;
	int max_incremental_steps=-1;
	int fixed_length=-1;
	object_pool_conf *system_pool;

	// inicializamos con los valores default de los system pools
	init_system_pools(cf);

	for (i=0; i<count; i++)
	{
		pool_conf = config_setting_get_elem(pool_setting, i);

		config_setting_lookup_string(pool_conf,"name",&name);

		if ((name != NULL) && (system_pool = get_system_pool_ref(&(cf->pool_conf), name)) != NULL)
		{
			CONF_FILE_APPLY_INT_VALUE((&(system_pool->fixed_length)),pool_conf,"fixed_length",(&fixed_length));
			CONF_FILE_APPLY_INT_VALUE((&(system_pool->incremental_length)),pool_conf,"incremental_length",&incremental_length);
			CONF_FILE_APPLY_INT_VALUE((&(system_pool->max_incremental_steps)),pool_conf,"max_incremental_steps",&max_incremental_steps);

		}


	}
	return 0;
}

static int conf_parse_servers(mashup_server_core_config_t *cf, config_setting_t *servers_setting)
{
	int count = config_setting_length(servers_setting), i=0;
	const config_setting_t *server_conf = NULL;
	mashup_server_endpoint_t *endpoint;
	const char*listen_ip;

	if (count > MAX_SERVERS)
	{
		return 1;
	}

	cf->server_configs.count=0;
	for (i=0; i<count; i++)
	{
		server_conf = config_setting_get_elem(servers_setting, i);
		endpoint = &(cf->server_configs.el[i]);
		endpoint->multiaccept = DEFAULT_MULTIACCEPT;

		config_setting_lookup_string(server_conf,"listen_ip",&(listen_ip));
		strcpy(endpoint->listen_ip,listen_ip);
		config_setting_lookup_int(server_conf,"listen_port",&(endpoint->listen_port));
		config_setting_lookup_int(server_conf,"tcp_backlog",&(endpoint->tcp_backlog));
		config_setting_lookup_int(server_conf,"multiaccept",&(endpoint->multiaccept));

		cf->server_configs.count++;
	}
	return 0;
}

static int conf_parse_priority_conf(mashup_server_core_config_t *cf, const config_setting_t *priority_setting)
{
	config_setting_lookup_int(priority_setting,"high",&(cf->q_conf.porcentaje_despacho_high));
	config_setting_lookup_int(priority_setting,"normal",&(cf->q_conf.porcentaje_despacho_normal));
	return 0;
}


static size_t get_user_pool_size(const char *function_name)
{
	user_pool_get_object_size fn_object_size = NULL;
	size_t ret = 0; // valor no valido usado para retornar cuando no se pudo cargar la funcion.

	if (jobs_controller_get_application_function((void **)&(fn_object_size), function_name) == 0)
	{
		ret = fn_object_size();
		if (ret < 0)
		{
			ret=0;
		}
	}
	return ret;
}



static int conf_parse_user_pools(mashup_server_core_config_t *cf, config_setting_t *pool_setting)
{
	int count = config_setting_length(pool_setting), i=0;
	config_setting_t *pool_conf = NULL;
	const char *name;
	const char *object_size_function;
	size_t object_size = 0;
	int incremental_length = -1;
	int max_incremental_steps = -1;
	int fixed_length = -1;

	int exclusive = 1; // default exclusive en true.
	object_pool_conf *user_pool;

	if (count + BASE_USER_POOLS > MAX_POOLS)
	{
		// loguear: demasiados user_pools.
		return 1;
	}
	for (i=0; i<count; i++)
	{
		pool_conf = config_setting_get_elem(pool_setting, i);

		config_setting_lookup_string(pool_conf,"name",&name);
		config_setting_lookup_string(pool_conf,"object_size",&object_size_function);
		config_setting_lookup_int(pool_conf,"incremental_length",&incremental_length);
		config_setting_lookup_int(pool_conf,"max_incremental_steps",&max_incremental_steps);
		config_setting_lookup_int(pool_conf,"fixed_length",&fixed_length);
		config_setting_lookup_bool(pool_conf,"exclusive",&exclusive);

		object_size = get_user_pool_size(object_size_function);
		if (object_size > 0)
		{
			user_pool = &(cf->pool_conf.el[cf->pool_conf.count]);
			strcpy(user_pool->name,name);
			user_pool->object_size = object_size;
			user_pool->exclusive = exclusive;

			CONF_FILE_APPLY_INT_VALUE((&(user_pool->fixed_length)),pool_conf,"fixed_length",&fixed_length);
			CONF_FILE_APPLY_INT_VALUE((&(user_pool->incremental_length)),pool_conf,"incremental_length",&incremental_length);
			CONF_FILE_APPLY_INT_VALUE((&(user_pool->max_incremental_steps)),pool_conf,"max_incremental_steps",&max_incremental_steps);

			/** Sumamos al array de pools este pool de usuario. **/
			cf->pool_conf.count++;

		}
		else
		{
			// loguear que no se pudo encontrar la funcion de tamanio o esta mal configurado el valor.
		}
	}
	return 0;
}

int conf_file_parse(mashup_server_core_config_t *cf, const char *filename)
{
	config_t cfg;
	config_setting_t *setting;

	memset(cf,0,sizeof(mashup_server_core_config_t));

	config_init(&cfg);

	if(! config_read_file(&cfg, filename))
	{
		fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg),
			config_error_line(&cfg), config_error_text(&cfg));
		config_destroy(&cfg);
		return(EXIT_FAILURE);
	}
	/** Archivo con formato correcto... cargamos las variables en cf. **/

	/** Cantidad de workers default. **/
	cf->workers = DEFAULT_WORKERS;
	config_lookup_int(&cfg, "workers", &(cf->workers));

	cf->worker_stack_size = DEFAULT_WORKER_STACK_SIZE;
	config_lookup_int(&cfg, "worker_stack_size", (int *)&(cf->worker_stack_size));
	/** pasamos a unidad de bytes. **/
	cf->worker_stack_size = cf->worker_stack_size * K;
	/** configuramos al server_worker su tamanio de stack. **/
	server_worker_set_stack_size(cf->worker_stack_size);

	/** Configuramos esquema de prioridad. Primero, los valores default. **/
	cf->q_conf.porcentaje_despacho_high=60;
	cf->q_conf.porcentaje_despacho_normal=40;

	setting = config_lookup(&cfg,"priority");
	if (setting != NULL)
	{
		conf_parse_priority_conf(cf,setting);
	}

	setting = config_lookup(&cfg,"system_pools");
	if (setting != NULL)
	{
		conf_parse_system_pools(cf,setting);
	}
	setting = config_lookup(&cfg,"user_pools");
	if (setting != NULL)
	{
		conf_parse_user_pools(cf,setting);
	}
	setting = config_lookup(&cfg,"servers");
	if (setting != NULL)
	{
		conf_parse_servers(cf,setting);
	}
    config_destroy(&cfg);
    return(EXIT_SUCCESS);
}

